#include "scene.h"

#include <QGraphicsRectItem>
#include <QAction>
#include "menubutton.h"
#include "toolbar.h"
#include "window.h"
#include <QTimer>
#include "application.h"

#include "scenetoolbardata.h"

extern Application* oApp;


class ScenePrivate{
public:
    ScenePrivate():toolbarsvisible(true){};
    ~ScenePrivate(){};

    bool m_wireframeEnabled;
    bool m_normalsEnabled;


    QColor m_backgroundColor;

    QTime m_time;
    int m_lastTime;
    int m_mouseEventTime;

    QGraphicsItem* mainMenu;

    float m_distance;
    Point3d m_rotation;
    Point3d m_angularMomentum;
    Point3d m_accumulatedMomentum;

    QList<SceneToolbarData*> toolbars;

    QGraphicsRectItem *lightItem;

    QTimer* hidetimer;
    QTimer* invisibletimer;
    bool toolbarsvisible;

    QPointF currentPos;
    QPointF lastPos;
};

void checkGLErrors(const QString& prefix)
{
    switch (glGetError()) {
    case GL_NO_ERROR:
        //qDebug() << prefix << tr("No error.");
        break;
    case GL_INVALID_ENUM:
        qDebug() << prefix << QObject::tr("Invalid enum.");
        break;
    case GL_INVALID_VALUE:
        qDebug() << prefix << QObject::tr("Invalid value.");
        break;
    case GL_INVALID_OPERATION:
        qDebug() << prefix << QObject::tr("Invalid operation.");
        break;
    case GL_STACK_OVERFLOW:
        qDebug() << prefix << QObject::tr("Stack overflow.");
        break;
    case GL_STACK_UNDERFLOW:
        qDebug() << prefix << QObject::tr("Stack underflow.");
        break;
    case GL_OUT_OF_MEMORY:
        qDebug() << prefix << QObject::tr("Out of memory.");
        break;
    default:
        qDebug() << prefix << QObject::tr("Unknown error.");
        break;
    }
}

Scene::Scene(QObject *parent)
    :QGraphicsScene(0, 0, 500, 500), O_PRIVATE_CREATE(Scene)
{
    O_D(Scene);
    d->m_wireframeEnabled = false;
    d->m_normalsEnabled = false;
    d->mainMenu = 0;
    d->m_backgroundColor = QColor(0, 170, 255);
    d->m_lastTime = 0;
    d->m_distance = 1.0f;
    d->m_angularMomentum = Point3d(0, 40, 0);

    QRadialGradient gradient(40, 40, 40, 40, 40);
    gradient.setColorAt(0.2, Qt::yellow);
    gradient.setColorAt(1, Qt::transparent);

    d->lightItem = new QGraphicsRectItem(0, 0, 80, 80);
    d->lightItem->setPen(Qt::NoPen);
    d->lightItem->setBrush(gradient);
    d->lightItem->setFlag(QGraphicsItem::ItemIsMovable);
    d->lightItem->setPos(800, 200);

    addItem(d->lightItem);
    d->m_time.start();

    d->hidetimer = new QTimer(this);
    connect(d->hidetimer, SIGNAL(timeout()), this, SLOT(toolbarsHide()));

    d->invisibletimer = new QTimer(this);
    connect(d->hidetimer, SIGNAL(timeout()), this, SLOT(checkMouse()));
    update();
}

Scene::~Scene()
{
    O_D(Scene);
    for(int i = 0; i < d->toolbars.size(); i++){
        delete d->toolbars[i];
    }
    O_PRIVATE_DELETE;
}


void Scene::setMainMenu(QGraphicsItem* item)
{
    O_D(Scene);
    d->mainMenu = item;
}

void Scene::updateDistance()
{
    O_D(Scene);
    qDebug() << d->m_distance;
    foreach(QGraphicsItem* i, items()){
        i->setScale(d->m_distance);
    }
}

void Scene::drawBackground(QPainter *painter, const QRectF &)
{
    //    if (painter->paintEngine()->type() != QPaintEngine::OpenGL
    //        && painter->paintEngine()->type() != QPaintEngine::OpenGL2)
    //    {
    //       // qWarning("OpenGLScene: drawBackground needs a QGLWidget to be set as viewport on the graphics view");
    //        return;
    //    }

    // painter->beginNativePainting();




    QTimer::singleShot(20, this, SLOT(update()));
}


void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    O_D(Scene);
    QGraphicsScene::mouseMoveEvent(event);
    //  if (event->isAccepted())
    //    return;

    d->currentPos = event->pos();
    d->invisibletimer->start(1200);

    //QTimer::singleShot(20, this, SLOT(visibleToolbars()));
    visibleToolbars();
    // qDebug() << QString("mouse move(x:%1, y:%2").arg(event->scenePos().x()).arg(event->scenePos().y());
    SceneToolbarData td;
    if (isOnToolbar(event->scenePos(), td)){
        d->hidetimer->stop();
        QTimer::singleShot(20, this, SLOT(toolbarsShow()));
    }else{
        d->hidetimer->start(500);
    }
}

void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    O_D(Scene);
    QGraphicsScene::mousePressEvent(event);
    if (event->isAccepted())
        return;

    if (event->buttons() == Qt::MidButton){
        d->m_distance = 1.0;
        updateDistance();
    }

    d->m_mouseEventTime = d->m_time.elapsed();
    d->m_angularMomentum = d->m_accumulatedMomentum = Point3d();
    event->accept();
}

void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    O_D(Scene);
    QGraphicsScene::mouseReleaseEvent(event);
    if (event->isAccepted())
        return;

    const int delta = d->m_time.elapsed() - d->m_mouseEventTime;
    d->m_angularMomentum = d->m_accumulatedMomentum * (1000.0 / qMax(1, delta));
    event->accept();
    update();
}

void Scene::wheelEvent(QGraphicsSceneWheelEvent *event)
{
    O_D(Scene);
    QGraphicsScene::wheelEvent(event);
    if (event->isAccepted())
        return;

    d->m_distance *= qPow(1.2, -event->delta() / 120);
    updateDistance();
    event->accept();
    update();
}

bool Scene::isOnToolbar(const QPointF& point, SceneToolbarData& td)
{
    O_D(Scene);
    foreach(SceneToolbarData* td, d->toolbars){
        QRectF r(td->toolbar->pos(), td->toolbar->boundingRect().size());
        if (r.contains(point))
            return true;
    }
    return false;
}

void Scene::checkMouse()
{
    O_D(Scene);
    if (d->lastPos == d->currentPos){
        invisibleToolbars();
    }else{
        visibleToolbars();
    }
    d->lastPos = d->currentPos;
}

void Scene::visibleToolbars()
{
    O_D(Scene);

    if (d->toolbarsvisible == true)
        return;

    d->invisibletimer->stop();

    for(int i = 0; i < d->toolbars.size(); i++){
//        if (d->toolbars[i]->place == Scene::flow)
//            continue;

        d->toolbars[i]->invisible->setStartValue(0.0f);
        d->toolbars[i]->invisible->setEndValue(1.0f);
        d->toolbars[i]->invisible->start();
        //d->toolbars[i]->toolbar->show();
    }
    d->toolbarsvisible = true;
}

void Scene::invisibleToolbars()
{
    O_D(Scene);
    if (d->toolbarsvisible == false)
        return;

    d->invisibletimer->stop();
    for(int i = 0; i < d->toolbars.size(); i++){
//        if (d->toolbars[i]->place == Scene::flow)
//            continue;

        d->toolbars[i]->invisible->setStartValue(1.0f);
        d->toolbars[i]->invisible->setEndValue(0.0f);
        d->toolbars[i]->invisible->start();;
        //d->toolbars[i]->toolbar->hide();
    }
    d->toolbarsvisible = false;
}

void Scene::toolbarsShow()
{
    O_D(Scene);
    for(int i = 0; i < d->toolbars.size(); i++){
        if (d->toolbars[i]->place == Scene::flow)
            continue;
        d->toolbars[i]->shown = true;
        d->toolbars[i]->toolbar->setHidden(false);
    }

    refresh();
}

void Scene::toolbarsHide()
{
    O_D(Scene);
    for(int i = 0; i < d->toolbars.size(); i++){
        if (d->toolbars[i]->place == Scene::flow)
            continue;
        d->toolbars[i]->shown = false;
        d->toolbars[i]->toolbar->setHidden(true);
    }

    refresh();
}

void Scene::refresh()
{
    O_D(Scene);
    for(int i = 0; i < d->toolbars.size(); i++){
        SceneToolbarData* td = d->toolbars[i];
        if (td->place == Scene::flow)
            return;

        QRectF r = td->toolbar->boundingRect();
        QRectF rc = td->parent == 0? sceneRect() : td->parent->boundingRect();
        switch(td->place){
        case top: //top
            td->toolbar->setPos(rc.width() / 2 - r.width() / 2, (td->shown ? 0 : -r.height() + 5));
            break;

        case bottom: //bottom
            td->toolbar->setPos(rc.width() / 2 - r.width() / 2, (td->shown ? rc.height() - r.height(): rc.height() - 5));
            break;

        case left: //left
            td->toolbar->setPos((td->shown ? 0 : -r.width() + 5), rc.height() / 2 - r.height() / 2);
            break;
        case right: //right
            td->toolbar->setPos((td->shown ? rc.width() - r.width() : rc.width() - 5), rc.height() / 2 - r.height() / 2);
            break;
        }
    }

}

void Scene::setActiveItem(QGraphicsItem* item)
{
    O_D(Scene);
    foreach(SceneToolbarData* t, d->toolbars){
        if (t->parent != NULL){
            if (t->parent == item)
                t->toolbar->setInvisible(false);
            else
                t->toolbar->setInvisible(true);
        }
    }
}

void Scene::installToolbar(Toolbar* toolbar, Holder place, int type, QGraphicsItem* parent)
{
    O_D(Scene);
    SceneToolbarData* td = new SceneToolbarData;
    td->parent = parent;
    td->toolbar = toolbar;
    td->place = place;
    td->shown = false;
    td->invisible = new QPropertyAnimation(td, "opacity");

    toolbar->setZValue(100);
    d->toolbars.append(td);
    addItem(toolbar);
    QTimer::singleShot(20, this, SLOT(refresh()));
}

void Scene::setToolbar(QList<QAction *> actions, Holder place, int type/* = 0*/)
{
    O_D(Scene);

    Toolbar* toolbar = new Toolbar((place == top || place == bottom)? Toolbar::horizontal: Toolbar::vertical);
    foreach(QAction* a, actions){
        toolbar->append(a);
    }
    installToolbar(toolbar, place, type);
}
